Manual Unpacking #4
|
:: Информация :: |
Pulse Reverseing Force |
|
|
Програма:
notepad.exe |
||
|
::: Съдържание :: |
||
|
В този туториал ще се запознаем с един друг криптатор,не толкова известен,но според мене заслужаващ нашето внимание.Ще разопаковаме(unpack-нем) едно notepad.exe криптирано с Yoda's Crypter v1.0.Но този път няма да се занимаем само с разопаковане а ще навлезем по-навътре и ще разберем как работи криптатора на Yado.Това което обикновенно се нарича revers-ване.А именно да разбереш как в действителност работи дадена програма. Както споменах по-горе при разопаковане на Yoda's Crypter v1.0 ще трябва да се сборим с малко анти-tracing,debugging код. В миналите ми туториали за MUP съм забравил да дам основните неща който трябва човек да спазва при разопаковане на дадено еxe.Затова сега разгледайте следните стъпки: 1.Прекъсване(спиране) на входната точка на програмата(PEP-Program Entry Point). 2.Преодоляване на защитния код(ако има такъв). 3.Достигане до оригиналната входна точка. 4.Записване на разопакованото вече в паметта exe на твърдия диск. 5.Поправяне оригиналната входна точка на записаното exe. 6.И ако пакера поврежда Import Table,IAT,Bound Imports или TLSTable e нужно да се поправят.(С нашия пример това няма да ни се наложи,само RVA и размера на Import Table ще променим) Първата стъпка можете да я изпълните по много начини.Когато имате само Softice,HexEditor и евентуално някои Pe Editor,можете да използвате метода на ръчно прекъсване на int3(Int3manual).Ако имате LordPe и Softice можете да използвате същия метод,но става много по-лесно(bpint3 в Softice и с дясното копче на мишката давате нафайла=>Break'n'Enter).Спирането на PEP можете да изпълните и като използвате SymbolLoader-a.Но при нас този проблем е решен още като стартираме notepad.exe с Ollydbg,защото Olly спира изпълнението на програмата на входната точка на пакера/програмата(PEP).Заредете в OllyDbg програмата notepad.exe и трябва да видите следното: yC:0040D060 pusha
//запазва всички регистри в
стека
yC:0040D061 call $+5 Какво представлява "Delta Offset"? Те взимат така нареченото делата разстояние(delta offset).За да можете да го разберете,първо трябва да обясня какво представлява delta offset.Използван е един много удобен начин за избягване използването на Base Relocation(основно пренасочване-когато файлът не се стартира на подразбиращия се адрес(Image Base)).Delta Offset e разликата между адреса на който трябва да бъде заредено exe-то в RAМ-а и адреса в RAM-a на който то се зарежда в действителност.За да се избегне използването на Base Relocation просто се взима тази разлика и се записва в някои регистър.После,когато искаме да използваме определен ресурс на някакво място в RAM-a ние няма да използваме само зададените оригинални адреси на който трябва да е дадената информация,а за да намерим тази информация ще трябва да съберем тази разлика Delta Offset с адреса посочен по подразбиране.По този начин ние ще попадаме винаги на точното място,когато искаме да вземем информация(или под форма на ресурси) заредена в RAM-а.За да ви стане по-ясно разгледайте следното: yC:0040D0FB add eax, dword ptr ss:(loc_401F12+2)[ebp] Това е код от пакнатия notepad,само че малко по-надолу.Забелязвате,че отстрани на адресите има [ebp].Това означава,че за да намерим вярното място,на което е търсената информация трябва към подразбиращия се адрес(401F18;401F14) да прибавим и стойността на ebp.Този начин на пренасочване е много ефективен.Използва се най-вече при вируси и при лоадери,защото най вече тогава се налага пренасочване.Обикновенно всяко нормално exe се зарейда на неговия по подразбиране адрес(Image Base),най-често 400000.Сега възниква въпроса как така всички exe-та се зареждат на тяхния по подразбиране адрес след като е почти невъзможно да се улучи точно адреса 400000?Отговорът е съвсем логичен и се съдържа в понятиято виртуална памет.Лоадера на windows създава при всяко стартиране на файл нов блок виртуална памет.И в крайна сметка се получават много такива блокове като всеки има адрес 400000,който не е зает.В паметта всяко exe си има тделен блок от жиртуална памет,така че обикновенно няма никаквозначение колко пъти ще бъде стартирана дадена програма защото тя не зависи от другите. Kак работи този метод?: Ето и основния принцип на работа ан този метод: Local GetIpCall // ---\ call GetIpCall // -------->Register-някой регистер:ebp,ebx,edx... GetIpCall: //----/ pop Register sub Register,offset GetIpCall
Call-ът на адрес 0040D061 извиква следващата функция(eip+5) като запомня адреса,на следващата инструкция в стека.Следващия ред възстановява стойността в регистър ebp.Стойността на ebp е 40D066 Ebp съдържа стойността 40D066,на който трябва да се зареди exe-то.Изважда се стойността по подразбиране с тази на която е зареден в действителност файлът и така се получава Delta Offset-а. Виждате трите поредни call-a?Нека да разгледаме първият от тях:0040D06D call sub_40D113.Ако влезете в него ще видите че той взима основните адреси(Base Address e с Image Base) на Api-тата LoadLibraryA и GetProcAddress,като взима Image Base отива на offset 3C(e_lfanew) от там разбира къде започва Pe Header и после прибавя 80 за да намери от кой адрес започва Import Table.След това намира в RAM-a адресите на Loadlibrary и GetProcAddress. Вторият 0040D072 call sub_40D148 взима адресите не други API функции.За да разберем на кои по точно е най-добре да влезем в call-а с F7 и да проследим какво прави програмата.Като влезете в този call ще видите как последователно се взимат адресите на "VirtualProtect" , "GetModuleFileNameA" , "CreateFileA" , "GlobalAlloc" , "GlobalFree" , "ReadFile" , "GetFileSize" и "CloseHandle".Всички тези функции са от kernel32.dll.Тези адреси се запазват за да бъдат използвани по-късно.Ако влезете в call-а ще видите че първо сe взима адреса на kernel32.dll и после се запаметява в esi.Ако влезете и в под-call-овете ще видите,че всички адреси се взимат с GetProcAddress като преди това има два push-a,където се задават параметрите на функцията GetProcaddress(първо се задава търсеното API,а после и адреса на DLL-a в който се намира).Всички адреси на Api-тата се запазват на различно място в паметта(ebp+някаква_стойност),за да могат по-късно да бъдат използвани.Ето и за улеснение съм взел информация за функцията GetProcAddress от win32.hlp: The GetProcAddress function returns the address of the specified exported dynamic-link library (DLL) function. Return Value Надявам се,че разбирате какво прави функцията,но за по сигурно ще обясня на български.Функцията GetProcAddress връща адреса на другата функця,която се търси и името и е запазено в стека.Нормално GetProcAddress връща адреса на функцията в рама,но ако се провали се връща нула.По този начин програмата взима адресите на всички необходими функции,който ще се използват после. Забележка:Всички параметри на API функции се запаметяват в стека в обратен ред,но там(в стека) те са подредени точно така както ги виждате в Win32.hlp. В следващия call:0040D077 call sub_40D201 се вика API-функцията VirtualProtect за да защити(предвиди) ново допълнително място(страници) в паметта за след разкриптирането.Тоест разширява SIzeOfImage за да няма проблеми при изпълнение на декриптираното exe.
yC:0040D07C test ss:(off_401F25+3)[ebp], 8 На реда 0040D07C test ss:(off_401F25+3)[ebp], 8 се сравнява стойноста на адрес 401F28+ebp с 8 и ако са равни скача на адрес 40D35F.Всъщност тези два реда проверяват дали сме пакнали exe-то с CRC опцията и ако сме го направили, функцията не скача на адрес 40D35F,а изпълняма call-a на адрес 40D088.Toзи call отваря файлът и му изчислява CRC-то.Ако искате да пропуснете този call може просто да преустановите флага за нулевия резултат когато сте още на адрес 40D086,което ще накара програмата да продължи изпълнението си от адрес 40D093 и ще прескочи call-a и следващата функция(след call-a)която запазва стойността върната от този call.Ако искате просто да преодолеете тази защита преустановете флага,но нека да види как точно се изчислява CRC-то на този бинарен файл. Първо би трябвало да кажа като за начало какво точно представлява CRC-то на даден файл.CRC означава в действителност Cyclic Redundancy Code.Това е стойност(уникална само за този файл),която служи при проверката дали файлът e идентичен.Може би ще се запитате за какво ни трябва да проверяваме дали файлът е идентичен.Ами отговорът не е чак толкова далеч.Понякога CRC-то служи като защитно средство срещу cracker-и.Когато кракваме дадена програма ние малко или много променяме нейния код и съответно нейното CRC.Даже и да не добавяме допълнителен код на програмата а само като променяме няколко байта(даречем je->jne,jmp->nop,jge->jnge и т.н.) понеже всеки файл е е еди дълъг niz от битове.(всеки файл е в формата на дълъг низ от нули и единици,но за по-лесно ние с Hex Editor го вждаме под формата на 16-десетичен niz)Даречем като променим JE-JNE ние променяме фактически HEX кода от 74 на 75 ние променяме 1110100 на 1110101.Следователно макар е с един бит ние променяме целия стринг(няма значение че не променяме дължината на бинарния низ(файлът)).Когато се пресмята CRC-то на кракнатия файл(променили сме фактически един бит 0 на 1) и се сравнява с CRC-то което е пресметнато когато файлът е компилиран(тоест оригиналния файл) и има вероятност да се засече разликата.Защо казвам има вероятност?Следователно може нашия байт 1 да премине през CRC проверката незасечен.За да разберем дали ще се засече нашия променен бит трябва да изпълним целия процес на изчисляване на CRC и да видим дали изчисленото CRC съвпада с оригиналното.За целта ще трябва да изучим начина по който се изчислява CRC-то на всеки файл. Забележка:Не при всеки файл се изчислява CRC.Когато видите с някой Pe Editor полето CheckSum да е 00000000,а дори и то да де е нули,когато не се сравнява с оригиналното можете да не се безпокойте за CRC проверка,защото такава няма да има....Това е разбира се въпрос на програмиста,дали ще сложи тази опция при създаването на EXE-то!При повечето файлове не се слага тази опция.....Поне така съм забелязал!При архивирането на файл с winace или winrar се изчислява почти винаги CRC.... Ако имате желание потърсете в нета за есета,който обесняват техниката на изчисляване на CRC.Аз ще се опитам да ви обесня нещата така както аз ги знам.Както казах,главната идея на CRC проверката е да разглежда файлът като един дълъг низ от битове(затова се наричат и бинарни файлове).Но какво правим след като разгледаме даден файл като дълъг низ от битове?Как трябва да изчислим CRC-то?За целта няма да взимаме истински файл(тоест нормален файл който е прекалено дълъг за обучителна цел),а ще работим с един низ не по дълъг от 32 бита.Първо трябва да спомена най-общо принципа на намиране на CRC.Изчисляването на CRC представлява последователно делене на нашия бинарен стринг(файл) с някакъв ключ,като накрая ще получим CRC-то на дадения файл.Но има някои особености при деленето на този низ,които ще разгледаме след малко.Даречем имаме файл с дулжина 64 кб и в бинарна форма той изглежда така: 10011011010010100101101000110110 Как можем да намерим CRC-то на този файл?За целта трябва да си изберем ключ с който да пресметнем CRC-то.Този ключ даречем ще бъде:110001(можем съвсем свободно да си избираме този ключ). Значи...нека да обобщим още веднъж!За целта трябва да разделим нашия примерен файл на нашия свободно избран ключ,като после ще получим евентуално остатък,който ще е нашето CRC(като казвам евентуално имам впредвид,че CRC-то може и да е нула-да няма остатък).Но при пресмятането на CRC се използва "специялна" аритметика(специялен начин на изваждане и събиране).Всъщтност деленето представлява Х пъти изваждане на нашия ключ от стринга от битове(файл). Нека като за начало да намерим CRC-тo по нормалните закони на математиката.Трябва да изваждаме ключът 110001 от бинарния низ докато получим остатъка. При деленето на низа 0011011010010100101101000110110 с ключа 110001 ще се получи остатък 10110.Ако искате можете да го изпробвате като в калкулатора на windows използвате инструкцията MOD-дава остатъка при делението на две числа.
Сега да видим как ще изглежда остатъка като приложим CRC аритметиката.Понеже при пресмятане на CRC се започва винаги от най-десния бит "1" Поради тази причина CRC-то,което ще получим с този ключ(110001) ще бъде с един бит по-малък от ключът.Фактически на края винаги се получава следното:прилага се изключващо или(XOR) на остатъка от последното "Изваждане"(без преноса,защото това е по-особено изваждане) и ключа.При последното изваждане се получава винаги: 1***** (остатъка при последното изваждане) \ xor -> *****(CRC) 1***** (нашия ключ) / Виждате от по горната схема,че ако пресмятаме CRC с ключ дълъг 6 бита(1*****) ще получим CRC дълго 5 бита(*****).Така когато изчисляваме CRC с ключ дълъг n-бита на брой ще получаваме винаги CRC с дължина n-1 бита на брой.Сега възниква въпросът:Ако имаме произволен файл най-вероятно е при последното изваждане да не останат точно n на брой бита които са равни на дължината на ключа.Тоест при деленето могат да останат неразделени битове.Поради тази причина,за да могат всички битове от файла да бъдат разделени се прибавят низ от нули който е дълъг с едно по малко от ключа.Разгледайте нашия пример по долу и как се извършва деленето.Ако искате за да се уверите можете да проверите че ако не добавим допълнителните нулеви битове няма да можем да разделим докрай низа. файл:0011011010010100101101000110110 / --- нулеви битове ключ:110001 ->дължината на този ключ е 5(а не 6) / файл след прибавянена нулеви битове:0011011010010100101101000110110|00000 <--001101101001010010110100011011000000 110001| | | | | | | | | --------v | | | | | | | | 000111100| | | | | | | | - 110001| | | | | | | | --------v | | | | | | | 00110110 | | | | | | | - 110001| | | | | | | ----------v | | | | | | 000111100| | | | | | - 110001| | | | | | ------------v | | | | | 00110110| | | | | - 110001| | | | | ------------v | | | | 000111110| | | | - 110001| | | | ---------------v | | | 00111110| | | - 110001| | | ------------v | | 00111100| | - 000000 | | -------------v | 000000110110| 110001| ------------v 000111000 110001 ------------------ 00100100 ---->CRC(остатък)
Както виждате CRC-то на този низ от битове(файл) е 100100.Принципът на изчисляване на CRC и на други бинарни файлове е същият.Може единствено да има разлика при самия ключ на който се дели и естествено файла.Тази технология на изваждане наподобява инструкцията XOR.Когато се изважда един бит от друг и когато има пренос той(преносът) се игнорира. Както забелязвате при всяко следващо изваждане се се започва винаги от най-крайния десен бит единица и се взимат толкова битове колкото трябват за да стане низа равен на ключа и да може да се извърши изваждането.В този случай остатъка е 100100 което е и CRC-то на този низ.Вижте следващата схема която пояснява начина на изваждане при този вид аритметика-CRC аритметиката.
0-0=0 \ ---->По принцип 0-1=-1,но понеже не се счита флага за пренос числото е положително. 0-1=1 === \ ---/ Начина по който се изваждат битове при CRC аритметиката 1-0=1 === / Не се взима под внимание флага за пренос 1-1=0 /
0+0=0 \ 0+1=1 === \ Така се събират числа по CRC аритметиката.Както забелязвате при 1+1 1+0=1 === / не се зачита флага за пренос.
1+1=0
/->няма пренос Всъщтност това си е най обикновенна аритметика само че флага за пренос не се взима в предвид.След като научихме как да изчисляваме CRC-то възниква въпросът как да си избираме ключа с който ще изчисляваме CRC.Предполагам сте чували за CRC-16 или CRC-32?Те са най-разпространените видове CRC проверки.При тези два вида ключът е или 17 битов или 33,за да се получи накрая 16 или 32 битов остатък CRC.След като възниква въпроса за избиране на ключ това означава че различните ключове ще имат различна продуктивност.Тоест един ключ ще е по добур от друг в засичането на грешки.Затова преди нас,по-мъдри хора са изследвали различните вариянти на ключове при 16 и 32 битови CRC проверки. Ето и най-разпространените ключове който се използват при CRC-16 и CRC-32:10001000000100001 и 100000100110000010001110110110111. Надявам се да сте добили представа какво представлява CRC и как се пресмята.За по подробно обяснение можете дапотърсите есета на англииски в www.google.com.След като смесе запознали най-общо с изчисляването на CRC е време да се върнем към разопаковането на notepad.exe и преодоляването на CRC защитата. Както обясних по рано за да прескочим CRC проверката ще преустановим нулевия флаг,когато сме на адрес 0040D086 и ще прескочим процедурата за изчисляване на CRC.Но нека да видим какво става действително ако не прескочим call-a за пресмятане на CRC.Влезте в кала с F7 и трябва да видите следния код: yC:0040D35F push 104h
//размера на буфера,където
ще се запише пътеката на файла yC:0040D36A push edi
//edi,съдържащ параметъра lpFilename
се съхранява се съхранява yC:0040D373 push 0
//този параметър е
игнориран Чрез API функцията GetModuleFileNameA се взима пътеката на файла,за да може той после да се отвори с API-то CreateFileA.Функцията CreateFileA връща отворения handle на файла,като той в случая е 0x0000001Ch.Информацията за различните API-функции на Windows съм взел от win32.hlp-можете да го намерите из нета,а различните константите от включения файл заедно с макро асемблер windows.inc.Естествено има и много други помощтни файлове за обяснение на API-функциите на windows.Нека да продължим с анализа на кода. yC:0040D395 mov edi, eax
//edi:=handle-a на файла(1C) Надявам се по-горните кометари да са ви ясни,но ако нещо не разбирате можете допълнително самостоятелно да потърсите обясненията в Win32.hlp за Api-тата на windows.Все пак нека да обобщим какво точно става в по-горния код.След като вече е взета пътеката на файла и той е отворен с CreateFileA и му е взет новия handle идва ред да се вземе неговия размер,който ще ни трябва при изчисляването на CRC и да се резервира блок(място) от паметта пак за пресмятането на CRC.Но в случай че функцията GlobalAlloc се провали се вика CloseHandle,която затваря Handle-a на отворения файл от CreateFIleA за да се възстанови всичко по старому,все едно не е отварян файла. yC:0040D3B3 xchg eax, ebx
//eax и ebx си разменят
стойностите,като вече ebx съдържа handle-a
на резервирания блок памет от GlobalAlloc yC:0040D3E2 mov edi, eax
//handle-a на блока от памет или мястото
където в паметта,където е зареден файла yC:0040D08D mov dword ptr ss:(loc_401F29+3)[ebp], eax запазва изчисленото CRC на адрес ss:401F2C.Ето и следващите редове: yC:0040D093 mov eax, dword ptr ss:(loc_401F12+2)[ebp]
//eax:=Image Base Ето и кода който трябвада видите като влезете в call-функцията: yC:0040D3F6 mov edi, eax
//edi:=Image Base yC:0040D48A mov edi, esi
//edi сочи началото на
секцията yC:0040D0A3 test ss:(off_401F25+3)[ebp], 1
//проверява дали сме
задали опцията за проверка за softice при
пакетирането yC:0040D1E9 push ebp
//запазва стойността на ebp в
стека Softice само ако имате опцията BoundsChecker(на Softice) зададена. 03 INT 03 C - CPU-generated - BREAKPOINT
03
INT 03 - Columbia PCs (desktop,VP portables) - ROM DEBUGGER
03
INT 03 - Realia COBOL - DEBUGGER SUPPORT
03
INT 03 U - Watcom WVIDEO, Watcom WD - OUTPUT DEBUGGING MESSAGE
03
INT 03 - DTown Utilities - POP UP
030000
INT 03 - Soft-ICE - BACK DOOR COMMANDS - GET Soft-ICE VERSION
0301
INT 03 - Soft-ICE - BACK DOOR COMMANDS - ???
030900
INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ???
030902
INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - POPUP & START A DEBUG
SESSION
030903
INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ???
030907
INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ???
03090A
INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ???
03090B
INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ???
030910
INT 03 - Soft-ICE - BACK DOOR COMMANDS - DISPLAY STRING IN Soft-ICE
WINDOW
030911
INT 03 - Soft-ICE - BACK DOOR COMMANDS - EXECUTE Soft-ICE COMMAND
030912
INT 03 - Soft-ICE - BACK DOOR COMMANDS - GET BREAKPOINT
INFORMATION
030913
INT 03 - Soft-ICE v2.5x - BACK DOOR COMMANDS - SET Soft-ICE
BREAKPOINT
030914
INT 03 - Soft-ICE v2.5x - BACK DOOR COMMANDS - REMOVE Soft-ICE
BREAKPOINT
030918
INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ???
0310
INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ???
0311
INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ???
03130C
INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ???
03130E
INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ???
031313
INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ???
030900
INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ???
Има
много начини за засичане на Softice.Тука е
използван само един от тях,което прави
пакнатия файл уязвим срещу кракери.Но
за да сте запознати и с другите,най-общи
начини за засичане на Softice ще се опитам
да ги обясня на кратко в този туториал.Трябва
да предупредя,че това изобщо не са
всички начини за засичане на Softice.Най-вероятно
има още много други,който не се
използват толкова често.Но вижте какво
съм ви предоставил аз:
Методи за засичане на Softice
Нека да се върнем към нашата основна задача:разопаковането на notepad.exe.Разгледахме защитата BoundsChecker.Като промените флага за нулев резултат на адрес:0040D0B7 ще продължи разопаковането на notepad.exe.В противен случай програмата излиза.Обръщам още веднъж внимани,че опцията Boundschacker трябва да е зададена,за да се засече Softice.Иначе не е нужно да се променя нулевия флаг. Ето и продължението на кода: yC:0040D0BB call sub_40D269 //в
този call се извършват операции с Import
Table-a;чете се всяко поле от Import Table-a В call-a на адрес 0040D0BB се извършват много операции.Взимат се последователно DLL-ите,като се чете Import Table-a.За даможете да разберете кода трябва да знаете малко повече за Import Table.Ето и кода който ще видите ако влезете в този call.Но аз ви препоръчвам да не се задълбавате чак толкова и просто да trace-нете над call-a с F8. yC:0040D269 mov eax, dword ptr ss:(loc_401F29+3)[ebp]
//eax=CRC yC:0040D33D cmp dword ptr [esi+10h], 0
//има ли някакъв DLL??
<<------------- ----------------------------- / yC:0040D291 mov ebx, [esi+0Ch]
//ebx съдържа адреса на DLL
<<--------
yC:0040D294 add ebx, dword ptr ss:(loc_401F12+2)[ebp]
//+ Image Base
От тези коментари не можете да разберете какво в действителност става.Но като цяло функцията на този call e да прочете Import Table-a и да замести всички попълнения в Import Table-a с техните основните адреси.За да можете да се ориентирате трябва да знаете много добре Import Table-a,нейната структура и т.н.Това мога да го обясня в някой друг туториал че този и без туй стана достатъчно дълъг.Но ако се интересувате има толкова много източници и материали на тема IT(Import Table). Oстанаха още 2 неща за преодоляване:опцията за изтриване на Pe Header-a и Import Table-a. Ето първата и съответно кода:
yC:0040D0C7 test ss:(off_401F25+3)[ebp], 2 //зададена
ли опциятаза изтриване на Pe Header-a? Ето и самото изтриване: yC:0040D22A mov edi, dword ptr ss:(loc_401F12+2)[ebp]
//edi:=Image Base
yC:0040D0D8 test ss:(off_401F25+3)[ebp], 4 //зададена
ли е опцията за изтриване на Import Table Ето кода,който трябва да видите ако влезете в самия call. yC:0040D243 pusha
//всички регистри се
запазват,защото ще бъдат използвани в
следващия код Сега е време да пристъпим към последната фаза,където няма преодоляване на защити.Ще намерим скока към оригиналната входна точка и ще запишем еxe-то директно от рама на твърдия диск. Ето и последната част от кода:
yC:0040D0F5 mov eax, dword ptr ss:(loc_401F16+2)[ebp]
//eax=OEP И готово exe-то работи!Като го дисасемблирате ще можете да видите истинския код на програмата и нейните ресурси. Последни
думи:
Нямам
представа тука какво трябва да пиша.Може
би нещо като обобщение;).Надявам се този
туториал да ви е бил полезен.Писал съм
за много други неща не само за самото
разопаковане.Този тут можеше да стане
много по-кратък,но този път реших да
беде нещо по-различно.Надявам се че ако
не ви е харесало,поне не ви е отегчило;))
Доста
е дълъг,а?;))
Int3-метод Ако
не сте запознати с този начин на
прекъсване на входната точка на пакера
прочетете следните редове. Условието
в началото беше,че имаме Softice и HexEditor,и
евентуално някакъв Pe Editor.Казвам
евентуално,защото Pe Editor-a служи за
улеснение,но ако можете да се оправяте
и без Pe Editor е още по добре. Аз
лично ще работя без Pe Editor за да стане по
интересно изследването.Много важно
нещото което трябва да го знаете и да се
съобразявате с него е,че всички
стойности,който виждате и ще видите
след малко с вашия Hex Editor са записани в
обратен ред. Първата
стъпка която трябва да изпълним е да
намерим Offset-a на входната точка на
програмата.След като знаеме,че в Optional
Header-a имаме член наречен AddressOfEntryPoint,където
е записано RVA-то(VA-ImageBase),където трябва
да започва изпълнението на програмата.Този
член е 7-мия от Optional Header.Вижте в winnt.h
какви са членовете преди AddressOfEntryPoint и
колко са дълги те,а ако ви мързи просто
ми се доверете и вижте следната
информация която аз ви давам.Това са
стандарти така че не е нужно да ги
знаете наизуст всичките а можете
просто да ги проверявате.
->Optional Header
Magic: 0x010B (HDR32_MAGIC) //1 word
MajorLinkerVersion: 0x03
//1 byte
MinorLinkerVersion: 0x0A -> 3.10 //1 byte
SizeOfCode: 0x00004000
//1 dword
SizeOfInitializedData: 0x00007400 //1 dword
SizeOfUninitializedData: 0x00000000 //1 dword
AddressOfEntryPoint: 0x0000D060 //1
dword
За
да намерим в Hex Editor-a къде се намира този
член AddressOfEntryPoint трябва да намерим
началото на Optional Header-a и да прибавим
байтовете до този член.Съберете
дължината на всички предходни
членове : Magic+MajorLinkerVersion+MinorLinkerVersion+SizeOfCode+SizeOfInitializedData+SizeOfUninitializedData=16
байта.Значи след като намерим началото
на Optional Header-a ще съберем 12 байта и ще
стигнем до нашата цел.Сега въпросат
който възниква е как можем да намерим
началото на Optional Header-a.Можем да го
разпознаем много лесно като търсим за
неговия първи член magic,който винаги
съдържа стойността 010B .Затова
потърсете във вашия Hex Editor тази
стойност,но в обратен ред:0B01
.Като намерите на кой
offset се намира тази стойност прибавете
16:98+16=АE>трябва да видите следната
стойност:60D00000-->това е RVA-то на което
трябва да започне изпълнението на
програмата.Но това е RVA=>адрес в
паметта а не на твърдия диск.За да
намерим входната точка на програмата в
Hex Editor-a трябва първо да определим
къмкоя секция принадлежи входната
точка,тоест от коя секция започва
изпълнението на кода.Съобразявайки се,
че по принцип SectionAligment e 1000,a FileAlignment - 200
ще намерим към коя секция трябва да се
причисли входната точка.Нека да
разгледаме Section Header-a на този файл.Но как
да стигнем до него?От Dos-Header-a разбираме
къде започва Pe Header-a като гледаме полето
e_lfanew:00000080
(в паметта)=>Pe Header-a започва от offset 80,прибавяме
към него размера на File Header-a -18//бройте
байтовете от началото на Pe Header докато
стигнете 0B01-магическа дума за Optional
Header//(със magic word-PE) и се озоваваме в
началото на Optional header-a,което е на offset 98.Сега
няма нужда да изчисляваме неговия
размер като бройм байтовете ред по ред
както направихме с File Header-a,a просто
погледнете полето в File Header SizeOfOptionalHeader,което
съдържа е E0 и като знаем че Section Table
започва след края на Pe Header(включително
и Data Directories) просто прибавяме към на
offset-a,от където започва Optional Header-a,а
именно 98 , еазмера на Optional header-a-E0 и като
резултат получаваме,че началото на Section
Table е 98+E0=178.От полето NumberOfSections в File Header
разбираме броя на секциите,а те са 6=> в Section Table ще
имаме 6 Header-a.Ето и структурата на всеки
Section Header- за всяка една секция от нашето exe
секця(стойностите са в обратен ред във
вашия Hex Editor):
Name:.text
Name:.data
Name:.idata VirtualAddress:1000
VirtualAddress:5000
VirtualAddress:6000 VirtualSize:3E9C
VirtualSize:84C
VirtualSize:DE8 RawOffset:1000
RawOffset:5000
RawOffset:6000 RawSize:4000
RawSize:1000
RawSize:1000 Flags:E0000020
Flags:C0000040
Flags:C0000040
Name:.rsrc
Name:.reloc
Name:yC VirtualAddress:7000
VirtualAddress:C000
VirtualAddress:D000 VirtualSize:4FB8
VirtualSize:A9C
VirtualSize:1000 RawOffset:7000
RawOffset:C000
RawOffset:D000 RawSize:5000
RawSize:1000
RawSize:547 Flags:C0000040
Flags:42000040
Flags:E00000E0 Ето
и как изглеждат Section Header-ите на тези
секции в Hex Editor-a: //маркираното
с черно е Section Header-a 1) 2) 3) 4) 5) 6) Ако
се вгледате по внимателно ще
забележите че местата на VirtualSize и
VirtualAddress,и на RawSize и RawOffset са разменени-тоест
с вашия Pe Editor вие виждате,че първо е
представен VirtualAddress,a после VirtualSize а в Hex
Editor-a е в обратен ред.Същото важи и за
RawSize и RawOffset. Нека
да се върнем към нашата цел за да
намерим към коя секция принадлежи
входящата точка ще сравняваме на всеки
Section Header стойността в полето VirtualAddress
с стойността от полето AddressOfEntryPoint,като
трябва да спазваме правилото:входната
точка трябва да е между началото на
секцията и нейния край(VirtualAddress<EntryPoint<VirtualAddress+VirtualSize).Нашата
входна точка е D060.Ето и сравняването: 1) .text:
1000<D060<1000+3E9C //не е
изпълнено 2) .data:
5000<D060<5000+84C //не
е изпълнено 3) .idata:
6000<D060<6000+DE8 //не
е изпълнено 4) .rsrc:
7000<D060<7000+4FB8 //не
е изпълнено 5) .reloc:
C000<D060<C000+A9C //не
е изпълнено 6) yC:
D000<D060<D000+1000 //изпълнено
е => входната точка
принадлежи към тази секция Сега
за да намерим Offset-a от който започва
изпълнението на програмата трябва да
извадим от входната точка VirtualAddress на
секзията,към която е входната точка-тоест
последната yC секция.После към
полученото число трябва да прибавим и
RawOffset на същата секция.Така вече ще
знаем offset-a,от който започва
изпълнението на програмата. EntryPoint-VirtualAddress+RawOffset
=> D060-D000+D000=D060 Offset-ът,който
получихме съвпада с стойността на
AddressOfEntryPoint.Сега излиза че вскичко,което
сме правили до момента е напразно.В
този случай,да!Но в повечето случаи
тези две стойности няма да са равни.Тука
се получава така,защото стойностите на
SectionAlignment и FileAlignment са равни(SectionAlignment=FileAlignment).Когато
видите че тези стойности са равни
можете да си спестите тези
изчислявания и просто да приемете
стойността на полето AddressOfEntryPoint като
Offset. Намерихме
адреса:D060 =>отидете на този Offset в Hex
Editor-a си и би трябвало да видите тези
първоначални байтове: 60
E8 00 00 => за да можем да
спрем изпълнението на програмата
трябва да сложим точка на прекъсване на
първия байт-тоест на мястото на 60.На
мястото на 60 трябва да сложим CC.Всъщтност
заменяме PUSHAD с INT3.Запаметете промяната
и излезте от Hex Editor-a. Сега
вече сме готови да прекъснем
изпълнението на програмата.Стартирайте
Softice напишете int3 и после <enter>.Стартирайте
exe-то и то трябва да спре на входната
точка.
Забележка:Не
забравяйте да възстановите стария код
като напишете е
eip 60. ---Shade----- Дата:
Автор:
Поща:
|
||